package vip.wangwenhao.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrResponse;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.StringUtils;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.springframework.util.CollectionUtils;
import vip.wangwenhao.annotation.Entity;
import vip.wangwenhao.annotation.FQParam;
import vip.wangwenhao.annotation.QParam;
import vip.wangwenhao.page.BasePage;
import vip.wangwenhao.page.PageResult;
import vip.wangwenhao.service.SolrService;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * solr接口默认实现类
 *
 * @author wangwenhao
 */
@Slf4j
public abstract class BaseSolrService<T, D> implements SolrService<T, D> {

    @Resource
    public SolrClient solrClient;
    private final static String Q_ALL = "*:*";
    private final static String DATA_IMPORT_PATH = "/dataimport";
    private final static String DATA_IMPORT_STATUS = "status";
    private final static String DATA_IMPORT_STATUS_SUCCESS = "idle";
    private final static String SEPARATION_COLON = ":";
    private final static Integer ZERO = 0;
    private final static Integer ONE = 1;
    private final static String ID = "id";

    @Override
    public void addOrUpdate(T t) {
        D d = bean2Document(t);
        try {
            solrClient.addBean(d);
            solrClient.commit();
        } catch (Exception e) {
            try {
                solrClient.rollback();
            } catch (Exception e1) {
                log.error("rollback|error|{}", JSONObject.toJSONString(d), e);
            }
            log.error("add|error|{}", JSONObject.toJSONString(d), e);
            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e.getMessage(), e);
        }
    }

    @Override
    public void delete(String id) {
        try {
            solrClient.deleteById(id);
            solrClient.commit();
        } catch (Exception e) {
            try {
                solrClient.rollback();
            } catch (Exception e1) {
                log.error("rollback|error|{}", id, e);
            }
            log.error("delete|error|{}", id, e);
            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e.getMessage(), e);
        }
    }


    @Override
    public T queryById(String id) {
        return queryByKeyAndValueLimitOne(ID, String.valueOf(id));
    }


    @Override
    public List<T> queryByKeyAndValue(String key, String value) {
        ArrayList<T> list = new ArrayList();
        try {
            SolrQuery params = new SolrQuery();
            params.setQuery(key + SEPARATION_COLON + value);
            QueryResponse query = solrClient.query(params);
            SolrDocumentList results = query.getResults();
            if (!CollectionUtils.isEmpty(results)) {
                list.add(fillObject(results.get(ZERO)));
            }
        } catch (Exception e) {
            log.error("query|error|{}={}", key, value, e);
            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e.getMessage(), e);
        }
        return list;
    }

    @Override
    public T queryByKeyAndValueLimitOne(String key, String value) {
        List<T> list = queryByKeyAndValue(key, value);
        if (!CollectionUtils.isEmpty(list)) {
            return list.get(ZERO);
        }
        return null;
    }

    @Override
    public PageResult<List<T>> query(BasePage query) {
        int count = 0;
        List<T> list = new ArrayList();
        try {
            SolrQuery params = new SolrQuery();
            log.info("q:{}", fillQParams(query));
            params.setQuery(fillQParams(query));
            log.info("fq:{}", JSONArray.toJSONString(fillFQParams(query)));
            params.setFilterQueries(fillFQParams(query));
            params.setStart(query.getStart());
            params.setRows(query.getPageSize());
            QueryResponse queryResponse = solrClient.query(params);
            SolrDocumentList results = queryResponse.getResults();
            if (query.isQueryCount()) {
                count = (int) results.getNumFound();
            }
            if (!CollectionUtils.isEmpty(results)) {
                for (SolrDocument solrDocument : results) {
                    list.add(fillObject(solrDocument));
                }
            }

        } catch (Exception e) {
            log.error("query|error|{}", JSONObject.toJSONString(query), e);
            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e.getMessage(), e);
        }
        return new PageResult<>(query.getPage(), query.getPageSize(), count, list);
    }

    /**
     * 解析结果文档到Java对象
     *
     * @param solrDocument
     * @return
     */
    public T fillObject(SolrDocument solrDocument) {
        Class<T> entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[ZERO];
        return JSONObject.parseObject(JSONObject.toJSONString(solrDocument), entityClass);
    }

    private String fillQParams(BasePage query) {
        JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(query));

        Field[] declaredFields = query.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            QParam qParam = field.getAnnotation(QParam.class);
            if (null != qParam) {
                Object q = jsonObject.get(field.getName());
                String qKey = field.getName();
                String value = qParam.value();
                if (!StringUtils.isEmpty(value)) {
                    qKey = value;
                }
                if (null != q) {
                    return qKey + SEPARATION_COLON + q;
                } else {
                    return Q_ALL;
                }
            }
        }
        return Q_ALL;
    }

    private String[] fillFQParams(BasePage query) {
        List<String> fqParams = new ArrayList();
        JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(query));
        Field[] declaredFields = query.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            FQParam fqParam = field.getAnnotation(FQParam.class);
            if (null != fqParam) {
                Object fq = jsonObject.get(field.getName());
                if (null != fq) {
                    String qKey = field.getName();
                    String value = fqParam.value();
                    if (!StringUtils.isEmpty(value)) {
                        qKey = value;
                    }
                    fqParams.add(qKey + SEPARATION_COLON + fq);
                }
            }
        }

        return fqParams.toArray(new String[ZERO]);
    }

    @Override
    public void dataImport() {
        try {
            Class<D> entityClass = (Class<D>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[ONE];
            Entity entity = entityClass.getAnnotation(Entity.class);
            if (null == entity) {
                throw new RuntimeException("document miss vip.wangwenhao.annotation.Entity");
            }
            NamedList<Object> result = solrClient.request(new SolrRequest(SolrRequest.METHOD.POST, DATA_IMPORT_PATH) {
                @Override
                public SolrParams getParams() {
                    HashMap<String, String> paramsMap = new HashMap();
                    paramsMap.put("command", "full-import");
                    paramsMap.put("clean", "true");
                    paramsMap.put("commit", "true");
                    paramsMap.put("wt", "json");
                    paramsMap.put("indent", "true");
                    paramsMap.put("entity", entity.value());
                    paramsMap.put("verbose", "false");
                    paramsMap.put("optimize", "false");
                    paramsMap.put("debug", "false");
                    return new MapSolrParams(paramsMap);
                }

                @Override
                protected SolrResponse createResponse(SolrClient solrClient) {
                    return null;
                }
            });
            while (!DATA_IMPORT_STATUS_SUCCESS.equals(result.get(DATA_IMPORT_STATUS))) {
                dataImport();
            }
        } catch (Exception e) {
            log.error("dataImport|error|", e);
            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e.getMessage(), e);
        }
    }

    public <T, D> D bean2Document(T t) {
        Class<D> entityClass = (Class<D>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[ONE];
        D d = JSONObject.parseObject(JSONObject.toJSONString(t), entityClass);
        return d;
    }

}